package org.xxpay.pay.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import javafx.util.Pair;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.util.DateUtil;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.common.util.WeightRandom;
import org.xxpay.core.entity.MchPayPassage;
import org.xxpay.core.entity.PayOrder;
import org.xxpay.core.entity.PayPassage;
import org.xxpay.core.entity.PayPassageAccount;
import org.xxpay.pay.mq.BaseNotify4MchPay;
import org.xxpay.pay.mq.queue.MqQueue4Statistics;

import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author: dingzhiwei
 * @date: 17/9/9
 * @description:
 */
@Service
public class PayOrderService {

    private static final MyLog _log = MyLog.getLog(PayOrderService.class);

    @Autowired
    private CommonService commonService;

    @Autowired
    public BaseNotify4MchPay baseNotify4MchPay;

    @Autowired
    public MqQueue4Statistics mqQueue4Statistics;

    public PayOrder query(Long mchId, String payOrderId, String mchOrderNo, boolean executeNotify) {
        PayOrder payOrder;
        if(StringUtils.isNotBlank(payOrderId)) {
            payOrder = commonService.payOrderService.selectByMchIdAndPayOrderId(mchId, payOrderId);
        }else {
            payOrder = commonService.payOrderService.selectByMchIdAndMchOrderNo(mchId, mchOrderNo);
        }
        if(payOrder == null) return null;

        if(executeNotify && (PayConstant.PAY_STATUS_SUCCESS == payOrder.getStatus() || PayConstant.PAY_STATUS_COMPLETE == payOrder.getStatus())) {
            baseNotify4MchPay.doNotify(payOrder, false);
            _log.info("业务查单完成,并再次发送业务支付通知.发送完成");
        }
        return payOrder;
    }

    public PayOrder query(Long mchId, String payOrderId, String mchOrderNo) {
        return query(mchId, payOrderId, mchOrderNo, false);
    }

    /**
     * 获取支付通道子账户
     * @param mchPayPassage
     * @param riskLog
     * @param amountL
     * @return
     */
    public Object getPayPassageAccount(MchPayPassage mchPayPassage, String riskLog, long amountL) {
        PayPassageAccount payPassageAccount = null;
        Integer productId = mchPayPassage.getProductId();
        Long mchId = mchPayPassage.getMchId();
        byte accountStrategy = mchPayPassage.getAccountStrategy(); //子账户轮询策略
        if(mchPayPassage.getIfMode() == 1) {    // 单独接口
            Integer payPassageId = mchPayPassage.getPayPassageId();
            if(mchPayPassage.getPayPassageAccountId() != null && mchPayPassage.getPayPassageAccountId() > 0) { //配置了指定子账户

                payPassageAccount = commonService.payPassageAccountService.findById(mchPayPassage.getPayPassageAccountId());
                PayPassage payPassage = commonService.payPassageService.findById(payPassageId);
                _log.info("[判断通道是否可用]payPassageId={},payPassageAccountId={}", payPassageId, mchPayPassage.getPayPassageAccountId());
                // 确定账号是否可用
                Object obj = getAvailablePPA(riskLog, amountL, payPassage, payPassageAccount);
                if(obj instanceof String) {
                    return obj;
                }
                if(obj instanceof PayPassageAccount) {
                    payPassageAccount = (PayPassageAccount) obj;
                }

            }else { //指定通道，未配置子账户

                Object obj = getPPA(riskLog, payPassageId, amountL, accountStrategy, mchPayPassage.getPollOrderTime());
                if(obj instanceof String) {
                    return obj;
                }
                if(obj instanceof PayPassageAccount) {
                    payPassageAccount = (PayPassageAccount) obj;
                }

            }
        }else if(mchPayPassage.getIfMode() == 2) {  // 轮询接口
            // 轮询通道参数
            String pollParam = mchPayPassage.getPollParam();
            if(StringUtils.isBlank(pollParam)) {
                return "没有配置轮询通道[productId="+productId+",mchId="+mchId+"]";
            }

            JSONArray array = JSONArray.parseArray(pollParam);
            if(CollectionUtils.isEmpty(array)) {
                return "配置的轮询通道为空[productId="+productId+",mchId="+mchId+"]";
            }
            // [{"payPassageId":1111,"weight":1},{"payPassageId":222,"weight":2}]
            List<Pair> pollPayPassList = new LinkedList<>();
            for(int i = 0; i < array.size(); i++) {
                JSONObject object = array.getJSONObject(i);
                int pid = object.getInteger("payPassageId");
                int weight = object.getInteger("weight");
                Pair pair = new Pair(pid, weight);
                pollPayPassList.add(pair);

            }

            // 递归取得可用通道账户
            Object obj = recursiveGetPPA(riskLog, pollPayPassList, amountL, accountStrategy, mchPayPassage.getPollOrderTime());
            if(obj instanceof String) {
                return obj;
            }
            if(obj instanceof PayPassageAccount) {
                payPassageAccount = (PayPassageAccount) obj;
            }
        }

        return payPassageAccount;
    }

    /**
     * 递归取子账户
     * @param riskLog
     * @param pollPayPassList
     * @param amountL
     * @return
     */
    private Object recursiveGetPPA(String riskLog, List<Pair> pollPayPassList, long amountL, byte accountStrategy, int pollOrderTime) {
        // 根据权重,随机取得支付通道
        if(CollectionUtils.isEmpty(pollPayPassList)) return null;
        WeightRandom weightRandom = new WeightRandom(pollPayPassList);
        int pid = (Integer) weightRandom.random();
        Object obj = getPPA(riskLog, pid, amountL, accountStrategy, pollOrderTime);
        if(null != obj) return obj;
        // 如果没有取到有效子账户,则从集合中删除,然后递归取
        final CopyOnWriteArrayList<Pair> cowList = new CopyOnWriteArrayList<>(pollPayPassList);
        for(Pair pair : cowList) {
            if(String.valueOf(pid).equals(String.valueOf(pair.getKey()))) {
                cowList.remove(pair);
            }
        }
        return recursiveGetPPA(riskLog, cowList, amountL, accountStrategy, pollOrderTime);
    }

    /**
     * 根据通道ID取子账户
     * @param riskLog
     * @param payPassageId
     * @param amountL
     * @param accountStrategy 新增子账户查询策略
     * @return
     */
    private Object getPPA(String riskLog, int payPassageId, long amountL, byte accountStrategy, int pollOrderTime) {
        // 判断支付通道
        PayPassage payPassage = commonService.payPassageService.findById(payPassageId);
        if(payPassage == null || payPassage.getStatus() != MchConstant.PUB_YES) {
            return "支付通道不存在或已关闭[payPassageId="+payPassageId+"]";
        }

        //查询该支付通道下子账户数量
        PayPassageAccount selectRecord = new PayPassageAccount();
        selectRecord.setPayPassageId(payPassageId);
        selectRecord.setStatus(MchConstant.PUB_YES);
        int ppaCount = commonService.payPassageAccountService.count(selectRecord);

        if(ppaCount <= 0) { //该支付通道下不存在任何子账户
            return "该支付通道没有配置可用子账户[payPassageId="+payPassageId+"]";
        }
        else if(ppaCount == 1){ //仅包含唯一1个子账户
            return getAvailablePPA(riskLog, amountL, payPassage,
                    commonService.payPassageAccountService.selectAllByPassageId(payPassageId).get(0));
        }

        List<PayPassageAccount> payPassageAccountList = null; //可用通道子账户集合 ： 根据下标依次获取可用通道

        //判断模式
        if(accountStrategy == PayConstant.ACCOUNT_STRATEGY_PAYORDER_TIME){ //根据下单时间

            payPassageAccountList = commonService.payPassageAccountService.selectAllByPayOrderTime(payPassageId);

        }else if(accountStrategy == PayConstant.ACCOUNT_STRATEGY_PAYORDER_COUNT){//根据订单笔数

            payPassageAccountList = commonService.payPassageAccountService.selectAllByPayOrderCount(payPassageId);

        }else if(accountStrategy == PayConstant.ACCOUNT_STRATEGY_PAYORDER_AMOUNT){ //根据订单金额

            payPassageAccountList = commonService.payPassageAccountService.selectAllByPayOrderAmount(payPassageId);

        }else if(accountStrategy == PayConstant.ACCOUNT_STRATEGY_RANDOM){ //随机

            // 得到可用的支付通道子账户 && 需要根据风控规则得到子账户号  && 随机打乱子账户顺序
            payPassageAccountList = commonService.payPassageAccountService.selectAllByPassageId(payPassageId);
            Collections.shuffle(payPassageAccountList);

        }else if(accountStrategy == PayConstant.ACCOUNT_STRATEGY_PAYORDER_POLL){ //根据子账号权重（按子账号订单总数排序）

            payPassageAccountList = commonService.payPassageAccountService.selectAllByPayOrderPollWeight(payPassageId, pollOrderTime);

        }else{
            return "子账户轮询方式配置有误";
        }

        for(PayPassageAccount ppa : payPassageAccountList) {
            Object obj = getAvailablePPA(riskLog, amountL, payPassage, ppa);
            if(obj == null) continue;
            if(obj instanceof String) {
                continue;
            }
            if(obj instanceof PayPassageAccount) {
                PayPassageAccount availablePayPassageAccount = (PayPassageAccount) obj;
                return availablePayPassageAccount;
            }
        }

        return null;
    }

    /**
     * 获取可用子账户(需判断账户状态,及风控)
     * @param riskLog
     * @param amountL
     * @param ppa
     * @return
     */
    private Object getAvailablePPA(String riskLog, long amountL, PayPassage payPassage, PayPassageAccount ppa) {
        // 判断通道状态
        if(payPassage == null) {
            _log.info("{}通道为空", riskLog);
            return "通道不存在";
        }
        if(payPassage.getStatus() != MchConstant.PUB_YES) {
            _log.info("{}通道状态停止[payPassageId={}]", riskLog, payPassage.getId());
            return "通道状态停止[payPassageId="+payPassage.getId()+"]";
        }
        // 判断账号状态为关闭
        if(ppa == null) {
            _log.info("{}子账号为空[payPassageId={}]", riskLog, payPassage.getId());
            return "子账号为空[payPassageId="+payPassage.getId()+"]";
        }
        if(ppa.getStatus() != MchConstant.PUB_YES) {
            _log.info("{}子账号状态停止[payPassageAccountId={}]", riskLog, ppa.getId());
            return "子账号状态停止[payPassageAccountId="+ppa.getId()+"]";
        }

        // 风控继承模式,设置继承的风控信息
        if(ppa.getRiskMode() == 1) {
            ppa.setRiskStatus(payPassage.getRiskStatus());
            ppa.setMaxDayAmount(payPassage.getMaxDayAmount());
            ppa.setMaxEveryAmount(payPassage.getMaxEveryAmount());
            ppa.setMinEveryAmount(payPassage.getMinEveryAmount());
            ppa.setTradeStartTime(payPassage.getTradeStartTime());
            ppa.setTradeEndTime(payPassage.getTradeEndTime());
        }
        // 判断风控状态为关闭,则返回账号对象
        if(ppa.getRiskStatus() == MchConstant.PUB_NO) {   // 风控状态是关闭的
            return checkPPA(payPassage, ppa, amountL);
        }
        // 下面逻辑判断是否触发风控规则,如果触发则返回空
        // 单笔交易金额是否满足
        long maxEveryAmount = ppa.getMaxEveryAmount();
        long minEveryAmount = ppa.getMinEveryAmount();
        if(amountL < minEveryAmount || amountL > maxEveryAmount) {
            _log.info("{}单笔交易金额触发风控.payPassageAccountId={},maxEveryAmount={},minEveryAmount={},amount={}", riskLog, ppa.getId(), maxEveryAmount, minEveryAmount, amountL);
            return "单笔金额应在:" + minEveryAmount/100 + " - " + maxEveryAmount/100 + "之间[payPassageAccountId="+ppa.getId()+"]";

        }
        // 交易时间是否满足
        String ymd = DateUtil.date2Str(new Date(), DateUtil.FORMAT_YYYY_MM_DD);
        String tradeStartTime = ymd + " " + (StringUtils.isBlank(ppa.getTradeStartTime()) ? "00:00:00" : ppa.getTradeStartTime());
        String tradeEndTime = ymd + " " + (StringUtils.isBlank(ppa.getTradeEndTime()) ? "23:59:59" : ppa.getTradeEndTime());

        long startTime = DateUtil.str2date(tradeStartTime).getTime();
        long endTime = DateUtil.str2date(tradeEndTime).getTime();
        long currentTime = System.currentTimeMillis();
        if(currentTime < startTime || currentTime > endTime) {
            _log.info("{}交易时间触发风控.payPassageAccountId={},tradeStartTime={},tradeEndTime={}", riskLog, ppa.getId(), tradeStartTime, tradeEndTime);
            return "交易时间应在每日:" + tradeStartTime + " - " + tradeEndTime + "之间[payPassageAccountId="+ppa.getId()+"]";
        }
        // 今日总交易金额是否满足
        long maxDayAmount = ppa.getMaxDayAmount();
        long dayAmount = commonService.payOrderService.sumAmount4PayPassageAccount(ppa.getId(),
                DateUtil.str2date(ymd + " 00:00:00"),
                DateUtil.str2date(ymd + " 23:59:59"));
        if(dayAmount > maxDayAmount) {
            _log.info("{}每日交易总额触发风控.payPassageAccountId={},maxDayAmount={},dayAmount={}", riskLog, ppa.getId(), maxDayAmount, dayAmount);
            return "超出今日收款总限额[payPassageAccountId="+ppa.getId()+"]";
        }
        return checkPPA(payPassage, ppa, amountL);
    }


    /** 判断是否有可用的转卡通道  **/
    private Object checkPPA(PayPassage payPassage, PayPassageAccount ppa, Long amount){

        if(payPassage.getConfigType() == MchConstant.PPA_CONFIG_JSON){  //json 配置， 正常返回


            if(!PayConstant.CHANNEL_NAME_ALIZFTPAY.equals(payPassage.getIfTypeCode())){
                return ppa;
            }

            //直付通， 需要获取到对应的二级商户
            Long qRPaySupport = PayConstant.PAY_CHANNEL_ALIZFTPAY_QR.equals(ppa.getIfCode()) ? 1L : null;
            Long appPaySupport = PayConstant.PAY_CHANNEL_ALIZFTPAY_APP.equals(ppa.getIfCode()) ? 1L : null;
            Long wapPaySupport = PayConstant.PAY_CHANNEL_ALIZFTPAY_WAP.equals(ppa.getIfCode()) ? 1L : null;
            Long pcPaySupport = PayConstant.PAY_CHANNEL_ALIZFTPAY_PC.equals(ppa.getIfCode()) ? 1L : null;

            String smid = commonService.payAlizftMchInfoService.getOneAvailableSmid(ppa.getTemplateId(), amount, qRPaySupport, appPaySupport, wapPaySupport, pcPaySupport);
            if(StringUtils.isEmpty(smid)){
                return "无可用二级商户[accountId="+ppa.getId()+"]"; //无可用银行卡
            }

            ppa.setPsVal("availableSMID", smid);
            return ppa;

        }

        return "configType配置有误[accountId="+ppa.getId()+"]"; //配置有误！

    }



}
